/* 
* Copyright (c) Harry Pierson. 
*
* This source code is subject to terms and conditions of the Microsoft 
* Permissive License. A copy of the license can be found in the License.html 
* file at the root of this distribution. If you cannot locate the  Microsoft 
* Permissive License, please send an email to harry@devhawk.net. By using this 
* source code in any fashion, you are agreeing to be bound by the terms of the 
* Microsoft Permissive License.
* 
* You must not remove this notice, or any other, from this software.
*/

using System;
using System.IO;
using System.CodeDom;
using System.Collections;
using System.Collections.Specialized;

namespace HawkWiki.WikiRenderer
{
    public class WikiGenerator
    {
        WikiRenderer _render;

        public WikiGenerator(TextReader tr)
        {
            _render = new WikiRenderer(tr);
        }


        public CodeNamespace GenerateCode(
            string namespaceName,
            string className,
            StringCollection refAssemblies)
        {
            //Init the assemblies collection & add the basic required assemblies
            refAssemblies.Clear();
            refAssemblies.Add("System.dll");
            refAssemblies.Add("System.web.dll");
            refAssemblies.Add("WikiRenderer.dll");

            //Create a new namespace to store this class in
            CodeNamespace codeNS = new CodeNamespace(namespaceName);

            //Import System.Web, System.Data.SQL & System.Xml namespaces
            codeNS.Imports.Add(new CodeNamespaceImport("System"));
            codeNS.Imports.Add(new CodeNamespaceImport("System.Web"));
            codeNS.Imports.Add(new CodeNamespaceImport("HawkWiki.WikiRenderer"));

            //Create a new wiki page class 
            CodeTypeDeclaration codePageClass = new CodeTypeDeclaration(className);
            codePageClass.BaseTypes.Add("HawkWiki.WikiRenderer.WikiPage");

            //Add wiki page class to namespace declared above 
            codeNS.Types.Add(codePageClass);

            //Add the skeleton for the ProcessWiki method
            CodeMemberMethod codeProcWiki = new CodeMemberMethod();
            codeProcWiki.Name = "ProcessWiki";
            codeProcWiki.Attributes = MemberAttributes.Public | MemberAttributes.Override;
            codeProcWiki.Parameters.Add(new CodeParameterDeclarationExpression("WikiWriter", "tw"));
            codePageClass.Members.Add(codeProcWiki);

            //add the skeleton for the ValidateWikiWords method
            CodeMemberMethod codeValPages = new CodeMemberMethod();
            codeValPages.Name = "ValidateWikiWords";
            codeValPages.Attributes = MemberAttributes.Private;
            codePageClass.Members.Add(codeValPages);

            //add code to ProcessWiki to initialize WikiWordExists and Link delegates
            //base.InitDelegates(tw)
            codeProcWiki.Statements.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(
                new CodeBaseReferenceExpression(), "InitDelegates",
                new CodeSnippetExpression("tw"))));

            //add a call in ProcessWiki to ValidateWikiPages
            codeProcWiki.Statements.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(
                new CodeThisReferenceExpression(), "ValidateWikiWords")));

            //Generate the code for the WikiPage subclass, based on the wiki text
            CodeWikiWriter cww = new CodeWikiWriter(codeProcWiki.Statements, codeValPages.Statements);
            _render.ProcessWiki(cww);

            //add a call at the end of ValidateWikiWords to the base classs version
            codeValPages.Statements.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(
                new CodeBaseReferenceExpression(), "RunWikiWordValidation")));

            return codeNS;



        }


        private class CodeWikiWriter : WikiWriter
        {
            CodeStatementCollection _codePW;
            CodeStatementCollection _codeVP;
            System.Text.StringBuilder _storedText = new System.Text.StringBuilder();
            StringCollection _wikiWords = new StringCollection();

            public CodeWikiWriter(CodeStatementCollection procWikiStmts, CodeStatementCollection procValidatePages)
            {
                _codePW = procWikiStmts;
                _codeVP = procValidatePages;
            }

            public override void WriteText(string text)
            {
                _storedText.Append(text);
            }

            private void WriteStoredText()
            {
                if (_storedText.Length == 0)
                    return;

                //tw.WriteText(_storedText)
                _codePW.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(
                    new CodeSnippetExpression("tw"), "WriteText",
                    new CodePrimitiveExpression(_storedText.ToString()))));
                _storedText.Remove(0, _storedText.Length);
            }
            public override void WriteWikiWord(string text)
            {
                WriteStoredText();
                //tw.WriteWikiWord(text)
                _codePW.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(
                    new CodeSnippetExpression("tw"), "WriteWikiWord",
                    new CodePrimitiveExpression(text))));

                //base.AddWikiWord(text)
                if (!_wikiWords.Contains(text))
                {
                    _wikiWords.Add(text);
                    _codeVP.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(
                        new CodeBaseReferenceExpression(), "AddWikiWord",
                        new CodePrimitiveExpression(text))));
                }
            }
            public override void WriteHyperlink(string url, string text)
            {
                WriteStoredText();
                //tw.WriteHyperlink(url, text)
                _codePW.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(
                    new CodeSnippetExpression("tw"), "WriteHyperlink",
                    new CodePrimitiveExpression(url), new CodePrimitiveExpression(text))));
            }
            public override void WriteImage(string url)
            {
                WriteStoredText();
                //tw.WriteImage(url)
                _codePW.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(
                    new CodeSnippetExpression("tw"), "WriteImage",
                    new CodePrimitiveExpression(url))));
            }
            public override void WriteFootnote(string text)
            {
                WriteStoredText();
                //tw.WriteFootnote(text)
                _codePW.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(
                    new CodeSnippetExpression("tw"), "WriteFootnote",
                    new CodePrimitiveExpression(text))));
            }

            public override void WriteFootnoteLink(string text)
            {
                WriteStoredText();
                //tw.WriteFootnoteLink(text)
                _codePW.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(
                    new CodeSnippetExpression("tw"), "WriteFootnoteLink",
                    new CodePrimitiveExpression(text))));
            }
            public override void WriteRule()
            {
                WriteStoredText();
                //tw.WriteRule()
                _codePW.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(
                    new CodeSnippetExpression("tw"), "WriteRule")));
            }

            public override void WriteNewline()
            {
                WriteStoredText();
                //tw.WriteNewline()
                _codePW.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(
                    new CodeSnippetExpression("tw"), "WriteNewline")));
            }

            public override void WriteBold(bool start)
            {
                WriteStoredText();
                //tw.WriteBold(start)
                _codePW.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(
                    new CodeSnippetExpression("tw"), "WriteBold",
                    new CodePrimitiveExpression(start))));
            }

            public override void WriteItalics(bool start)
            {
                WriteStoredText();
                //tw.WriteItalics(start)
                _codePW.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(
                    new CodeSnippetExpression("tw"), "WriteItalics",
                    new CodePrimitiveExpression(start))));
            }

            public override void WriteParagraph(bool start)
            {
                WriteStoredText();
                //tw.WriteParagraph(start)
                _codePW.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(
                    new CodeSnippetExpression("tw"), "WriteParagraph",
                    new CodePrimitiveExpression(start))));
            }
            public override void WriteListItem(bool start)
            {
                WriteStoredText();
                //tw.WriteListItem(start)
                _codePW.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(
                    new CodeSnippetExpression("tw"), "WriteListItem",
                    new CodePrimitiveExpression(start))));
            }
            public override void WriteMonospaced(bool start)
            {
                WriteStoredText();
                //tw.WriteMonospaced(start)
                _codePW.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(
                    new CodeSnippetExpression("tw"), "WriteMonospaced",
                    new CodePrimitiveExpression(start))));
            }

            public override void WriteIndent(bool start)
            {
                WriteStoredText();
                //tw.WriteIndent(start)
                _codePW.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(
                    new CodeSnippetExpression("tw"), "WriteIndent",
                    new CodePrimitiveExpression(start))));
            }

            public override void WriteHeading(int level, bool start)
            {
                WriteStoredText();
                //tw.WriteHeading(level, start)
                _codePW.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(
                    new CodeSnippetExpression("tw"), "WriteHeading",
                    new CodePrimitiveExpression(level), new CodePrimitiveExpression(start))));
            }

            public override void WriteList(ListType type, bool start)
            {
                WriteStoredText();
                //tw.WriteList(type, start)
                _codePW.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(
                    new CodeSnippetExpression("tw"), "WriteList",
                    new CodeSnippetExpression("WikiWriter.ListType." + type.ToString()), new CodePrimitiveExpression(start))));
            }

        }



    }
}
